#include "client.h"
#include "logger.h"
#define TRAIN_SIZE 10240

// 收发文件火车箱

char file_data[TRAIN_SIZE];

int main(int argc, char *argv[])
{
    // 参数 ./client ip port
    ARGS_CHECK(argc, 3);
    // 准备建立tcp连接
    struct sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr(argv[1]);
    // 创建socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    // 建立日志系统
    log_init(LOG_INFO, "log.txt"); // 初始化日志系统，输出到 stderr

    // 连接服务器
    connect(sockfd, (struct sockaddr *)&server, sizeof(server));
    // 接收一个服务端的回应
    char buf[4096] = {0};
    train_t train;
    // 验证用户名和密码
    // printf("please input 1 for login, 2 for register: ");
    int choice = 0;
    // scanf("%d", &choice);
    // getchar(); // 清除换行符
    char userName[128] = {0};

    while (1)
    {
        printf("please input 1 for login, 2 for register: ");
        scanf("%d", &choice);
        getchar(); // 清除换行符

        if (choice == 1)
        {
            // 发送登录命令
            bzero(&train, sizeof(train));
            train.train_len = sizeof(choice);
            memcpy(train.train_data, &choice, sizeof(choice));
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送登录命令
            LOG_INFO("send choice: %d\n", choice);

            // 登录
            bzero(&train, sizeof(train));
            printf("please input userName: ");
            fgets(userName, sizeof(userName), stdin);
            userName[strlen(userName) - 1] = '\0'; // 去掉换行符
            train.train_len = strlen(userName);
            memcpy(train.train_data, userName, train.train_len);

            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送用户名

            LOG_INFO("send userName: %s\n", train.train_data);

            // 发送密码
            bzero(&train, sizeof(train));
            printf("please input password: ");
            fgets(train.train_data, sizeof(train.train_data), stdin);
            train.train_data[strlen(train.train_data) - 1] = '\0'; // 去掉换行符
            train.train_len = strlen(train.train_data);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送密码
            LOG_INFO("send password success.\n");
            // 接收服务器返回的数据
            int ret_login = -1;
            bzero(&train, sizeof(train));
            recv_n(sockfd, &train.train_len, sizeof(train.train_len)); // 接收火车的长度
            printf("train_len = %d\n", train.train_len);
            recv_n(sockfd, &train.train_data, train.train_len); // 接收火车的内容
            memcpy(&ret_login, train.train_data, sizeof(ret_login));
            printf("train_data = %d\n", ret_login); // 打印火车的内容
            LOG_INFO("recv from server: %d\n", ret_login);
            if (ret_login == 1)
            {
                printf("login success!\n");
                break;
            }
            else
            {
                printf("login failed!\n");
            }
        }
        else if (choice == 2)
        {
            // 发送注册命令
            bzero(&train, sizeof(train));
            train.train_len = sizeof(choice);
            memcpy(train.train_data, &choice, sizeof(choice));
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送登录命令
            LOG_INFO("send choice: %d\n", choice);
            // 注册
            // 发送用户名
            bzero(&train, sizeof(train));
            printf("please input userName: ");
            fgets(train.train_data, sizeof(train.train_data), stdin);
            train.train_data[strlen(train.train_data) - 1] = '\0'; // 去掉换行符
            train.train_len = strlen(train.train_data);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送用户名
            LOG_INFO("send userName: %s\n", train.train_data);
            // 发送密码
            bzero(&train, sizeof(train));
            printf("please input password: ");
            fgets(train.train_data, sizeof(train.train_data), stdin);
            train.train_data[strlen(train.train_data) - 1] = '\0'; // 去掉换行符
            train.train_len = strlen(train.train_data);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送密码

            LOG_INFO("send password: %s\n", train.train_data);
            LOG_INFO("register success!\n");
            // 接收服务器返回的数据
            bzero(&train, sizeof(train));
            recv_n(sockfd, &train.train_len, sizeof(train.train_len)); // 接收火车的长度
            printf("train_len = %d\n", train.train_len);
            recv_n(sockfd, &train.train_data, train.train_len); // 接收火车的内容
            printf("train_data = %s\n", train.train_data);      // 打印火车的内容
            LOG_INFO("recv from server: %s\n", train.train_data);
            printf("recv from server: %s\n", train.train_data);
            printf("----------------------------------\n");
            // 继续循环可以选择登录
            continue;
        }
        else
        {
            printf("input error!\n");
            // close(sockfd);
            continue;
        }
    }

    // 接收第一次服务器返回数据
    recv_n(sockfd, &train.train_len, sizeof(train.train_len)); // 接收火车的长度
    // printf("train_len = %d\n", train.train_len);
    recv_n(sockfd, &train.train_data, train.train_len); // 接收火车的内容
    bzero(buf, sizeof(buf));
    memcpy(buf, train.train_data, train.train_len);
    // 打印火车的内容
    // 第一次要返回主页的文件"ls"命令

    LOG_INFO("recv from server: %s\n", train.train_data);

    printf("----------------------------------\n");
    char cmd[1024] = {0};
    char cmd_dup[1024] = {0};
    while (1)
    {
        // 接收用户输入
        printf("----------------------------------\n");
        bzero(cmd, sizeof(cmd));
        bzero(cmd_dup, sizeof(cmd_dup));
        printf("#%s@-:", userName);
        printf("%s:", buf);
        fgets(cmd, sizeof(cmd), stdin);
        cmd[strlen(cmd) - 1] = '\0'; // 去掉换行符
        LOG_INFO("input cmd: %s\n", cmd);
        memcpy(cmd_dup, cmd, strlen(cmd));
        LOG_INFO("cmd_dup: %s\n", cmd_dup);
        /*
        1. cd 进入对应目录
        2. ls 列出相应目录文件
        3. puts 将本地文件上传至服务器
        4. gets 文件名 下载服务器文件到本地
        5. remove 删除服务器上文件
        6. pwd 显示目前所在路径
        7. 其他命令可以自己添加（mkdir 等等）
        8. 客户端可以退出（quit）
        9. 无效命令不响应
        */
        // 分词

        char *p = strtok(cmd, " ");
        if (strcmp(p, "quit") == 0)
        { // 退出并发送退出命令
            printf("client quit\n");
            train.train_len = strlen(p);
            strcpy(train.train_data, p);

            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0);
            // sleep(1); // 睡一秒让对方有时间处理
            break;
        }
        else if (strcmp(p, "cd") == 0)
        { // 进入对应目录
            bzero(&train, sizeof(train));
            train.train_len = strlen(cmd);
            strcpy(train.train_data, cmd);

            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送cmd命令

            // printf("%s\n",train.train_data);

            // 接收服务器返回数据

            bzero(&train, sizeof(train));
            recv_n(sockfd, &train.train_len, sizeof(train.train_len));
            // printf("train_len = %d\n",train.train_len);
            recv_n(sockfd, train.train_data, train.train_len);
            // 规范化打印
            strcat(buf, train.train_data);
            printf("recv from server:\n");
            char *p = strtok(buf, "\n");
            printf("%s\n", p);
            int i = 1;
            while ((p = strtok(NULL, " ")) != NULL)
            {
                printf("%-18s\t", p);
                if (i % 4 == 0)
                {
                    printf("\n");
                }
                ++i;
            }
            printf("\n");

            // printf("%s\n",train.train_data);
            // printf("recv from server:\n %s\n", train.train_data);
        }

        // 列出相应目录文件
        else if (strcmp(p, "ls") == 0)
        {
            train.train_len = strlen(cmd);
            strcpy(train.train_data, cmd);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送ls命令
            // 接收服务器返回数据,几个文件名
            bzero(&train, sizeof(train));
            recv_n(sockfd, &train.train_len, sizeof(train.train_len));
            recv_n(sockfd, &train.train_data, train.train_len);
            int num = 0;
            memcpy(&num, train.train_data, sizeof(num));
            LOG_INFO("recv filenum: %d\n", num);
            if (num == 0)
            {
                printf("dir is empty!\n");
                continue;
            }

            // 接收文件名,并打印
            bzero(buf, sizeof(buf)); // buf装需要打印的信息
            for (int i = 0; i < num; i++)
            {   
                bzero(buf, sizeof(buf));
                bzero(&train, sizeof(train));
                recv_n(sockfd, &train.train_len, sizeof(train.train_len));
                recv_n(sockfd, &train.train_data, train.train_len);
                memcpy(buf, train.train_data, train.train_len);
                // sprintf(buf, "%-8s\t", " ");
                strcat(buf, "\t");
                printf("%s", buf);
                if (i != 0 && i % 5 == 0 && i != num - 1)
                {
                    printf("\n");
                }
            }
            printf("\n");
            // 规范化打印
            // bzero(buf, sizeof(buf));
            // strcat(buf, train.train_data);
            // printf("%s\n", buf);
            // 接收目录
            bzero(buf, sizeof(buf));
            bzero(&train, sizeof(train));
            recv_n(sockfd, &train.train_len, sizeof(train.train_len));
            recv_n(sockfd, &train.train_data, train.train_len);
            LOG_INFO("recv from server:%s\n", train.train_data);
            memcpy(buf, train.train_data, train.train_len);
            continue;
        }
        else if (strcmp(p, "put") == 0)
        { // 上传本地文件至服务器

            p = strtok(NULL, " "); // 获取文件名,并先验证文件是否存在
            if (access(p, F_OK) == -1)
            {
                printf("file %s not exist\n", p);
                continue;
            }
            // 判断文件是否是文件
            struct stat st;
            stat(p, &st);
            if (S_ISDIR(st.st_mode))
            {
                printf("path %s is a directory\n", p);
                continue;
            }

            // 计算文件的哈希值
            const char *filepath = p;
            char hex_hash[SHA256_HEX_LENGTH]; // 准备足够大的缓冲区

            if (calculate_file_sha256(filepath, hex_hash) == 0)
            {
                LOG_INFO("calculate_file_sha256 success\n");
                LOG_INFO("SHA-256 Hash of '%s':\n%s\n", filepath, hex_hash);
            }
            else
            {
                LOG_ERROR("calculate_file_sha256 failed\n");
                LOG_ERROR("Failed to calculate SHA-256 hash for '%s'.\n", filepath);
                return 1;
            }

            // 发送put命令
            bzero(&train, sizeof(train));
            train.train_len = strlen(cmd_dup);
            strcpy(train.train_data, cmd_dup);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送puts命令
            LOG_INFO("send cmd: %s\n", train.train_data);

            // 发送哈希值
            bzero(&train, sizeof(train));
            train.train_len = strlen(hex_hash);
            strcpy(train.train_data, hex_hash);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送哈希值
            LOG_INFO("send hash: %s\n", train.train_data);

            // 接收服务器返回数据，是否存在，是否可以传输
            // 若是存在则接收1，不存在则接收0，发生错误则返回-1
            int re = 1;
            recv_n(sockfd, &re, 4);
            if (re == 1)
            {
                LOG_INFO("recv from server re = %d\n", re);
                printf("put file %s success.\n", p);
                LOG_INFO("put file %s success.\n", p);
                // continue;
            }
            else if (re == -1)
            {
                LOG_INFO("recv from server re = %d\n", re);

                printf("file etransfer error.\n");
                LOG_ERROR("file etransfer error.\n");
                // continue;
            }
            else if (re == 1)
            {
                LOG_INFO("recv from server re = %d\n", re);

                printf("put file %s success.\n", p);
                LOG_INFO("recv a signal of put file %s success.\n", p);
                // continue;
            }
            // 文件存在则开始传输文件
            else if (re == 0)
            {
                LOG_INFO("recv from server re = %d\n", re);

                // 发送文件大小
                int fd = open(p, O_RDWR);
                ERROR_CHECK(fd, -1, "open");
                ssize_t filesize = lseek(fd, 0, SEEK_END);
                lseek(fd, 0, SEEK_SET);
                bzero(&train, sizeof(train));
                train.train_len = sizeof(filesize);
                memcpy(train.train_data, &filesize, train.train_len);
                send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送文件大小
                LOG_INFO("send filesize: %ld\n", filesize);
                // 发送文件
                ssize_t currentsize = 0;
                int slice = filesize / 1000;
                if (slice == 0)
                {
                    slice = 1;
                }
                int oldslice = 0;
                while (filesize > currentsize)
                {
                    int ret = read(fd, file_data, TRAIN_SIZE);
                    if (ret == -1)
                    {
                        perror("read");
                        return -1;
                    }
                    send(sockfd, file_data, ret, 0);
                    currentsize += ret;
                    if (currentsize - oldslice >= slice)
                    {
                        oldslice = currentsize;
                        printf("\rsend to server: %3ld%%", currentsize * 100 / filesize);
                        fflush(stdout);
                    }
                }
                printf("\rsend to server: 100%%\n");
                LOG_INFO("send file %s success.\n", p);
                close(fd);
                // 接收服务器返回数据

                // recv_n(sockfd, &train.train_len, sizeof(train.train_len));
                // recv_n(sockfd, &train.train_data, train.train_len);
                // memcpy(&re, &train.train_data, train.train_len);
                // LOG_INFO("recv from server: %d\n", re);

                printf("put file %s success.\n", p);
            }
            // 接收目录
            bzero(buf, sizeof(buf));
            recv_n(sockfd, &train.train_len, sizeof(train.train_len));
            recv_n(sockfd, &train.train_data, train.train_len);
            LOG_INFO("recv from server:%s\n", train.train_data);
            memcpy(buf, train.train_data, train.train_len);
            continue;
        }
        else if (strcmp(p, "get") == 0)
        { // 下载服务器文件到本地
            train.train_len = strlen(cmd);
            strcpy(train.train_data, cmd);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送gets命令
            // 接收服务端返回数据，是否存在，是否可以传输
            // 若是存在则接收1，不存在则接收-1，是文件夹则返回0,发生错误则返回-2
            int re = 1;
            recv_n(sockfd, &re, 4);
            if (re == -1)
            {
                printf("file not exist\n");
                continue;
            }
            else if (re == 0)
            {
                printf("file exist, but a dir.\n");
                continue;
            }
            else if (re == -2)
            {
                printf("file etransfer error.\n");
                continue;
            }
            {
                printf("start recving!\n");
            }

            // 接收文件
            // recv_file(sockfd);
        }
        else if (strcmp(p, "remove") == 0)
        { // 删除服务器上文件
            train.train_len = strlen(p);
            strcpy(train.train_data, p);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送remove命令
            // 发送文件名
            bzero(&train, sizeof(train));
            p = strtok(NULL, " ");
            train.train_len = strlen(p);
            strcpy(train.train_data, p);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送文件名
            // 接收服务器返回数据
            recv_n(sockfd, &train.train_len, sizeof(train.train_len));
            recv_n(sockfd, &train.train_data, train.train_len);
            if (strcmp(train.train_data, "0") == 0)
            {
                printf("file not exist\n");
                continue;
            }
            else if (strcmp(train.train_data, "1") == 0)
            {
                printf("file exist, start to remove\n");
            }
        }
        else if (strcmp(p, "pwd") == 0)
        { // 显示目前所在路径
            train.train_len = strlen(p);
            strcpy(train.train_data, p);
            send(sockfd, &train, sizeof(train.train_len) + train.train_len, 0); // 发送pwd命令
            // 接收服务器返回数据
            bzero(&train, sizeof(train));
            recv_n(sockfd, &train.train_len, sizeof(train.train_len));
            recv_n(sockfd, &train.train_data, train.train_len);
            memcpy(&buf, &train.train_data, train.train_len);
            printf("recv from server:\n %s\n", buf);
        }
        else
        { // 其他命令不响应
            printf("invalid command\n");
        }
    }
    sleep(1);
    close(sockfd);
    return 0;
}